]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Use a Tweener "Frame Ticker" with a ClutterTimeline backend
authorOwen Taylor <otaylor@src.gnome.org>
Sun, 23 Nov 2008 04:12:34 +0000 (04:12 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Sun, 23 Nov 2008 04:12:34 +0000 (04:12 +0000)
Call Tweener.setFrameTicker() with a custom object that bridges to
ClutterTimeline to get new frame notifications. Combined with a
hack to dynamically adjust the frame ticker's frame rate when
Clutter drops frames, this means that our animations play in the
intended time even if rendering is too slow to maintain a full
60HZ frame rate.

http://bugzilla.gnome.org/show_bug.cgi?id=561745

svn path=/trunk/; revision=79

js/ui/main.js

index 6b394e8e494bad83ca289ec1b84630537c27a00a..c52a2460b10a2048b3ed2dc76cfe5eb31b47e8a1 100644 (file)
@@ -1,7 +1,9 @@
 /* -*- mode: js2; js2-basic-offset: 4; -*- */
 
-const Shell = imports.gi.Shell;
 const Clutter = imports.gi.Clutter;
+const Shell = imports.gi.Shell;
+const Signals = imports.signals;
+const Tweener = imports.tweener.tweener;
 
 const Panel = imports.ui.panel;
 const Overlay = imports.ui.overlay;
@@ -16,9 +18,76 @@ let overlay = null;
 let run_dialog = null;
 let wm = null;
 
+// The "FrameTicker" object is an object used to feed new frames to Tweener
+// so it can update values and redraw. The default frame ticker for
+// Tweener just uses a simple timeout at a fixed frame rate and has no idea
+// of "catching up" by dropping frames.
+//
+// We substitute it with custom frame ticker here that connects Tweener to
+// a Clutter.TimeLine. Now, Clutter.Timeline itself isn't a whole lot more
+// sophisticated than a simple timeout at a fixed frame rate, but at least
+// it knows how to drop frames. (See HippoAnimationManager for a more
+// sophisticated view of continous time updates; even better is to pay
+// attention to the vertical vblank and sync to that when possible.)
+//
+function ClutterFrameTicker() {
+    this._init();
+}
+
+ClutterFrameTicker.prototype = {
+    TARGET_FRAME_RATE : 60,
+
+    _init : function() {
+       // We don't have a finite duration; tweener will tell us to stop
+       // when we need to stop, so use 1000 seconds as "infinity"
+       this._timeline = new Clutter.Timeline({ fps: this.TARGET_FRAME_RATE,
+                                               duration: 1000*1000 });
+       this._frame = 0;
+
+       let me = this;
+       this._timeline.connect('new-frame',
+           function(timeline, frame) {
+               me._onNewFrame(frame);
+           });
+    },
+
+    _onNewFrame : function(frame) {
+       // Unfortunately the interface to to send a new frame to tweener
+       // is a simple "next frame" and there is no provision for signaling
+       // that frames have been skipped or just telling it the new time.
+       // But what it actually does internally is just:
+       //
+       //  _currentTime += 1000/_ticker.FRAME_RATE;
+       //
+       // So by dynamically adjusting the value of FRAME_RATE we can trick
+       // it into dealing with dropped frames.
+
+       let delta = frame - this._frame;
+       if (delta == 0)
+           this.FRAME_RATE = this.TARGET_FRAME_RATE;
+       else
+           this.FRAME_RATE = this.TARGET_FRAME_RATE / delta;
+       this._frame = frame;
+       this.emit('prepare-frame');
+    },
+
+    start : function() {
+       this._timeline.start();
+    },
+
+    stop : function() {
+       this._timeline.stop();
+       this._frame = 0;
+    }
+};
+
+Signals.addSignalMethods(ClutterFrameTicker.prototype);
+
 function start() {
     let global = Shell.global_get();
 
+    Tweener.setFrameTicker(new ClutterFrameTicker());
+
     // The background color really only matters if there is no desktop
     // window (say, nautilus) running. We set it mostly so things look good
     // when we are running inside Xephyr.